/**
 * 项目名称:
 *
 * @Author: 夏国倩
 * @Date: 2023/10/16 16:03
 * @Copyright
 */
package JDBC;

import java.sql.*;
import java.util.Hashtable;
import java.util.Map;
import java.util.Scanner;

/*
1.解决SQL注入的问题
  只要用户提供的信息不参与SQL语句的编译过程，问题就解决了
  即使用户提供的信息中含有SQL语句的关键字，但是没有参与编译，不起作用
  要想用户信息不参与SQL语句的编译，那么必须使用java.sql.PreparedStatement
  PreparedStatement 接口继承了java.sql.Statement
  PreparedStatement 是属于预编译的数据库操作对象
  PreparedStatement 的原理是：预先对SQL语句的框架进行编译，然后再给SQL语句块传”值“
2.测试
  用户名：fafa
  密码： fafa' or '1=1
  登录失败
3.解决SQL注入的关键是：
  用户提供的信息中即使含有SQL语句的关键字，但是这些关键字不参与编译，不起作用
4.对比一下Statement 和 PreparedStatement
   Statement 存在SQL注入问题  PreparedStatement解决了SQL注入问题
   Statement 是编译一次执行一次 PreparedStatement是编译一次，可执行N次， PreparedStatement效率更高
   PreparedStatement 会在编译阶段做类型的安全检查

   综上所述：PreparedStatement使用较多，只有极少数情况需要使用Statement
 */
public class JDBCtest7 {
    public static void main(String[] args) {
        //初始化一个界面，用于接收用户输入的用户信息
        Map<String, String> userLoginInfo = initUI();
        //验证用户名和密码
        boolean loginSuccess = login(userLoginInfo);
        //最后输出结果
        System.out.println(loginSuccess?"登录成功":"登录失败");
    }

    /**
     * 用户登录
     * @param userLoginInfo 用户登录信息
     * @return  false 表示失败，true表示成功
     */
    private static boolean login(Map<String, String> userLoginInfo) {
        //打标记的意识
        boolean loginSuccess=false;
        //单独定义变量
        String loginName=userLoginInfo.get("loginName");
        String loginPwd=userLoginInfo.get("loginPwd");

        //JDBC代码
        Connection conn=null;
        PreparedStatement ps=null; //这里使用PreparedStatement(预编译的数据库操作对象)
        ResultSet rs=null;

        try{
            //1. 注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //2.获取连接
            conn = DriverManager.getConnection(
                    "jdbc:mysql://localhost:3306/base6?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC",
                    "root",
                    "123456");
            //3.获取预编译的数据库操作对象
            //SQL语句中的 ？ 表示占位符，一个 ？ 将来接收一个”值“，注意：占位符不能使用单引号括起来
            String sql="select * from t_user where loginName =? and loginPwd =?";
            ps=conn.prepareStatement("sql"); //当程序执行到此，会发送SQL语句到DBMS，然后DBMS进行SQL语句的预先编译
            //给占位符 ？ 赋值（第一个占位符的下标是1，第二个占位符的下标是2，JDBC 中所有下标从1开始）
            ps.setString(1,loginName);
            ps.setString(2,loginPwd);
            //ps.setInt(3,33);
            //4.执行SQL
            rs=ps.executeQuery();
            //5.处理结果集
            if (rs.next()) {
                //登录成功
                loginSuccess = true;
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //6.释放资源
            if (rs != null){
                try{
                    rs.close();
                }catch (Exception e){
                    throw new RuntimeException(e);
                }
            }
            if (ps != null){
                try{
                    ps.close();
                }catch (Exception e){
                    throw new RuntimeException(e);
                }
            }
            if (conn != null){
                try{
                    conn.close();
                }catch (Exception e){
                    throw new RuntimeException(e);
                }
            }

        }
        //查询
        return loginSuccess;
    }

    /**
     * 初始化用户界面
     *
     * @return 用户输入的用户名和密码信息
     */
    private static Map<String, String> initUI() {
        Scanner s = new Scanner(System.in);

        System.out.println("用户名：");
        String loginName = s.nextLine();

        System.out.println("密码：");
        String loginPwd = s.nextLine();

        Map<String, String> userLoginInfo = new Hashtable<>();
        userLoginInfo.put("loginName", loginName);
        userLoginInfo.put("loginPwd", loginPwd);


        return userLoginInfo;
    }
}
